﻿#  AzureWebSitePublishModule.psm1 è un modulo script di Windows PowerShell che esporta le funzioni Windows PowerShell che automatizzano la gestione del ciclo di vita delle applicazioni Web. Tali funzioni possono essere utilizzate con o senza personalizzazione per l'ambiente dell'applicazione e di pubblicazione.

Set-StrictMode -Version 3

# Variabile per il salvataggio della sottoscrizione originale.
$Script:originalCurrentSubscription = $null

# Variabile per il salvataggio dell'account di archiviazione originale.
$Script:originalCurrentStorageAccount = $null

# Variabile per il salvataggio dell'account di archiviazione della sottoscrizione specificata dall'utente.
$Script:originalStorageAccountOfUserSpecifiedSubscription = $null

# Variabile per il salvataggio del nome di sottoscrizione.
$Script:userSpecifiedSubscription = $null


<#
.SYNOPSIS
Antepone data e ora a un messaggio.

.DESCRIPTION
Antepone data e ora a un messaggio. Questa funzione è progettata per messaggi scritti nei flussi Error e Verbose.

.PARAMETER  Message
Specifica i messaggi di base senza la data.

.INPUTS
System.String

.OUTPUTS
System.String

.EXAMPLE
PS C:\> Format-DevTestMessageWithTime -Message "Aggiunta del file $filename alla directory"
2/5/2014 1:03:08 PM - Aggiunta del file $filename alla directory

.LINK
Write-VerboseWithTime

.LINK
Write-ErrorWithTime
#>
function Format-DevTestMessageWithTime
{
    [CmdletBinding()]
    param
    (
        [Parameter(Position=0, Mandatory = $true, ValueFromPipeline = $true)]
        [String]
        $Message
    )

    return ((Get-Date -Format G)  + ' - ' + $Message)
}


<#

.SYNOPSIS
Scrive un messaggio di errore che ha come prefisso l'ora corrente.

.DESCRIPTION
Scrive un messaggio di errore che ha come prefisso l'ora corrente. Questa funzione chiama la funzione Format-DevTestMessageWithTime per anteporre l'ora prima di scrivere il messaggio nel flusso Error.

.PARAMETER  Message
Specifica il messaggio in una chiamata di messaggio di errore. È possibile utilizzare la stringa del messaggio per la funzione.

.INPUTS
System.String

.OUTPUTS
Nessuno. La funzione scrive nel flusso Error.

.EXAMPLE
PS C:> Write-ErrorWithTime -Message "Failed. Cannot find the file."

Write-Error: 2/6/2014 8:37:29 AM - Failed. Cannot find the file.
 + CategoryInfo     : NotSpecified: (:) [Write-Error], WriteErrorException
 + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException

.LINK
Write-Error

#>
function Write-ErrorWithTime
{
    [CmdletBinding()]
    param
    (
        [Parameter(Position=0, Mandatory = $true, ValueFromPipeline = $true)]
        [String]
        $Message
    )

    $Message | Format-DevTestMessageWithTime | Write-Error
}


<#
.SYNOPSIS
Scrive un messaggio dettagliato che ha come prefisso l'ora corrente.

.DESCRIPTION
Scrive un messaggio dettagliato che ha come prefisso l'ora corrente. Poiché chiama Write-Verbose, il messaggio viene visualizzato solo quando lo script è eseguito con il parametro Verbose o quando la preferenza VerbosePreference è impostata su Continue.

.PARAMETER  Message
Specifica il messaggio in una chiamata di messaggio dettagliato. È possibile utilizzare la stringa del messaggio per la funzione.

.INPUTS
System.String

.OUTPUTS
Nessuno. La funzione scrive nel flusso Verbose.

.EXAMPLE
PS C:> Write-VerboseWithTime -Message "The operation succeeded."
PS C:>
PS C:\> Write-VerboseWithTime -Message "The operation succeeded." -Verbose
VERBOSE: 1/27/2014 11:02:37 AM - The operation succeeded.

.EXAMPLE
PS C:\ps-test> "The operation succeeded." | Write-VerboseWithTime -Verbose
VERBOSE: 1/27/2014 11:01:38 AM - The operation succeeded.

.LINK
Write-Verbose
#>
function Write-VerboseWithTime
{
    [CmdletBinding()]
    param
    (
        [Parameter(Position=0, Mandatory = $true, ValueFromPipeline = $true)]
        [String]
        $Message
    )

    $Message | Format-DevTestMessageWithTime | Write-Verbose
}


<#
.SYNOPSIS
Scrive un messaggio host dettagliato che ha come prefisso l'ora corrente.

.DESCRIPTION
Questa funzione scrive un messaggio al programma host (Write-Host) che ha come prefisso l'ora corrente. L'effetto della scrittura al programma host può variare. La maggior parte dei programmi che ospitano Windows PowerShell scrive tali messaggi nell'output standard.

.PARAMETER  Message
Specifica il messaggio di base senza la data. È possibile utilizzare la stringa del messaggio per la funzione.

.INPUTS
System.String

.OUTPUTS
Nessuno. La funzione scrive il messaggio nel programma host.

.EXAMPLE
PS C:> Write-HostWithTime -Message "Operazione riuscita."
1/27/2014 11:02:37 AM - Operazione riuscita.

.LINK
Write-Host
#>
function Write-HostWithTime
{
    [CmdletBinding()]
    param
    (
        [Parameter(Position=0, Mandatory = $true, ValueFromPipeline = $true)]
        [String]
        $Message
    )
    
    if ((Get-Variable SendHostMessagesToOutput -Scope Global -ErrorAction SilentlyContinue) -and $Global:SendHostMessagesToOutput)
    {
        if (!(Get-Variable -Scope Global AzureWebAppPublishOutput -ErrorAction SilentlyContinue) -or !$Global:AzureWebAppPublishOutput)
        {
            New-Variable -Name AzureWebAppPublishOutput -Value @() -Scope Global -Force
        }

        $Global:AzureWebAppPublishOutput += $Message | Format-DevTestMessageWithTime
    }
    else 
    {
        $Message | Format-DevTestMessageWithTime | Write-Host
    }
}


<#
.SYNOPSIS
Restituisce $true se una proprietà o il metodo è un membro dell'oggetto. In caso contrario, $false.

.DESCRIPTION
Restituisce $true se la proprietà o il metodo è un membro dell'oggetto. Questa funzione restituisce $false per metodi statici della classe e per visualizzazioni, quali PSBase e PSObject.

.PARAMETER  Object
Specifica l'oggetto nel test. Immettere una variabile che contiene un oggetto o un'espressione che restituisce un oggetto. Non è possibile specificare tipi, come [DateTime] o utilizzare oggetti per questa funzione.

.PARAMETER  Member
Specifica il nome della proprietà o metodo nel test. Quando si specifica un metodo, omettere le parentesi che seguono il nome del metodo stesso.

.INPUTS
Nessuno. Questa funzione non riceve input dalla pipeline.

.OUTPUTS
System.Boolean

.EXAMPLE
PS C:\> Test-Member -Object (Get-Date) -Member DayOfWeek
True

.EXAMPLE
PS C:\> $date = Get-Date
PS C:\> Test-Member -Object $date -Member AddDays
True

.EXAMPLE
PS C:\> [DateTime]::IsLeapYear((Get-Date).Year)
True
PS C:\> Test-Member -Object (Get-Date) -Member IsLeapYear
False

.LINK
Get-Member
#>
function Test-Member
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [Object]
        $Object,

        [Parameter(Mandatory = $true)]
        [String]
        $Member
    )

    return $null -ne ($Object | Get-Member -Name $Member)
}


<#
.SYNOPSIS
Restituisce $true se la versione del modulo Azure è 0.7.4 o successiva. In caso contrario, $false.

.DESCRIPTION
Test-AzureModuleVersion restituisce $true se la versione del modulo Azure è 0.7.4 o successiva. Se il modulo non è installato o se la versione è precedente, viene restituito $false. Questa funzione non dispone di parametri.

.INPUTS
Nessuno

.OUTPUTS
System.Boolean

.EXAMPLE
PS C:\> Get-Module Azure -ListAvailable
PS C:\> #No module
PS C:\> Test-AzureModuleVersion
False

.EXAMPLE
PS C:\> (Get-Module Azure -ListAvailable).Version

Major  Minor  Build  Revision
-----  -----  -----  --------
0      7      4      -1

PS C:\> Test-AzureModuleVersion
True

.LINK
Get-Module

.LINK
PSModuleInfo object (http://msdn.microsoft.com/en-us/library/system.management.automation.psmoduleinfo(v=vs.85).aspx)
#>
function Test-AzureModuleVersion
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [System.Version]
        $Version
    )

    return ($Version.Major -gt 0) -or ($Version.Minor -gt 7) -or ($Version.Minor -eq 7 -and $Version.Build -ge 4)
}


<#
.SYNOPSIS
Restituisce $true se la versione installata del modulo Azure è 0.7.4 o successiva.

.DESCRIPTION
Test-AzureModule restituisce $true se la versione del modulo Azure installato è 0.7.4 o successiva. Restituisce $false se il modulo non è installato o se la versione è precedente. Questa funzione non dispone di parametri.

.INPUTS
Nessuno

.OUTPUTS
System.Boolean

.EXAMPLE
PS C:\> Get-Module Azure -ListAvailable
PS C:\> #No module
PS C:\> Test-AzureModule
False

.EXAMPLE
PS C:\> (Get-Module Azure -ListAvailable).Version

Major  Minor  Build  Revision
-----  -----  -----  --------
    0      7      4      -1

PS C:\> Test-AzureModule
True

.LINK
Get-Module

.LINK
PSModuleInfo object (http://msdn.microsoft.com/en-us/library/system.management.automation.psmoduleinfo(v=vs.85).aspx)
#>
function Test-AzureModule
{
    [CmdletBinding()]

    $module = Get-Module -Name Azure

    if (!$module)
    {
        $module = Get-Module -Name Azure -ListAvailable

        if (!$module -or !(Test-AzureModuleVersion $module.Version))
        {
            return $false;
        }
        else
        {
            $ErrorActionPreference = 'Continue'
            Import-Module -Name Azure -Global -Verbose:$false
            $ErrorActionPreference = 'Stop'

            return $true
        }
    }
    else
    {
        return (Test-AzureModuleVersion $module.Version)
    }
}


<#
.SYNOPSIS
Salva la sottoscrizione Windows Azure corrente nella variabile $Script:originalSubscription nell'ambito dello script.

.DESCRIPTION
La funzione Backup-Subscription salva la sottoscrizione di Windows Azure corrente (Get-AzureSubscription -Current) e il relativo account di archiviazione, nonché la sottoscrizione modificata da questo script ($UserSpecifiedSubscription) e il relativo account di archiviazione, nell'ambito dello script. Salvando i valori, è possibile utilizzare una funzione quale Restore-Subscription per ripristinare la sottoscrizione corrente originale e l'account di archiviazione nello stato corrente se lo stato corrente è stato modificato.

.PARAMETER UserSpecifiedSubscription
Specifica il nome della sottoscrizione in cui verranno create e pubblicate le nuove risorse. La funzione salva i nomi della sottoscrizione e dei relativi account di archiviazione nell'ambito dello script. Questo parametro è obbligatorio.

.INPUTS
Nessuno

.OUTPUTS
Nessuno

.EXAMPLE
PS C:\> Backup-Subscription -UserSpecifiedSubscription Contoso
PS C:\>

.EXAMPLE
PS C:\> Backup-Subscription -UserSpecifiedSubscription Contoso -Verbose
VERBOSE: Backup-Subscription: Start
VERBOSE: Backup-Subscription: Original subscription is Windows Azure MSDN - Visual Studio Ultimate
VERBOSE: Backup-Subscription: End
#>
function Backup-Subscription
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [string]
        $UserSpecifiedSubscription
    )

    Write-VerboseWithTime 'Backup-Subscription: inizio'

    $Script:originalCurrentSubscription = Get-AzureSubscription -Current -ErrorAction SilentlyContinue
    if ($Script:originalCurrentSubscription)
    {
        Write-VerboseWithTime ('Backup-Subscription: la sottoscrizione originale è ' + $Script:originalCurrentSubscription.SubscriptionName)
        $Script:originalCurrentStorageAccount = $Script:originalCurrentSubscription.CurrentStorageAccountName
    }
    
    $Script:userSpecifiedSubscription = $UserSpecifiedSubscription
    if ($Script:userSpecifiedSubscription)
    {        
        $userSubscription = Get-AzureSubscription -SubscriptionName $Script:userSpecifiedSubscription -ErrorAction SilentlyContinue
        if ($userSubscription)
        {
            $Script:originalStorageAccountOfUserSpecifiedSubscription = $userSubscription.CurrentStorageAccountName
        }        
    }

    Write-VerboseWithTime 'Backup-Subscription: fine'
}


<#
.SYNOPSIS
Ripristina lo stato "corrente" della sottoscrizione Windows Azure salvata nella variabile $Script:originalSubscription nell'ambito dello script.

.DESCRIPTION
La funzione Restore-Subscription imposta la sottoscrizione salvata nella variabile $Script:originalSubscription come sottoscrizione corrente (nuovamente). Se la sottoscrizione originale presenta un account di archiviazione, questa funzione rende tale account di archiviazione corrente per la sottoscrizione corrente. La funzione ripristina la sottoscrizione solo se è presente una variabile $SubscriptionName non-null nell'ambiente. In caso contrario termina la propria esecuzione. Se $SubscriptionName è popolato ma $Script:originalSubscription è $null, Restore-Subscription utilizza il cmdlet Select-AzureSubscription per cancellare le impostazioni Current e Default per le sottoscrizioni in Windows Azure PowerShell. Questa funzione non dispone di parametri, non accetta input e non restituisce risultati (void). È possibile utilizzare -Verbose per scrivere messaggi nel flusso Verbose.

.INPUTS
Nessuno

.OUTPUTS
Nessuno

.EXAMPLE
PS C:\> Restore-Subscription
PS C:\>

.EXAMPLE
PS C:\> Restore-Subscription -Verbose
VERBOSE: Restore-Subscription: Start
VERBOSE: Restore-Subscription: End
#>
function Restore-Subscription
{
    [CmdletBinding()]
    param()

    Write-VerboseWithTime 'Restore-Subscription: inizio'

    if ($Script:originalCurrentSubscription)
    {
        if ($Script:originalCurrentStorageAccount)
        {
            Set-AzureSubscription `
                -SubscriptionName $Script:originalCurrentSubscription.SubscriptionName `
                -CurrentStorageAccountName $Script:originalCurrentStorageAccount
        }

        Select-AzureSubscription -SubscriptionName $Script:originalCurrentSubscription.SubscriptionName
    }
    else 
    {
        Select-AzureSubscription -NoCurrent
        Select-AzureSubscription -NoDefault
    }
    
    if ($Script:userSpecifiedSubscription -and $Script:originalStorageAccountOfUserSpecifiedSubscription)
    {
        Set-AzureSubscription `
            -SubscriptionName $Script:userSpecifiedSubscription `
            -CurrentStorageAccountName $Script:originalStorageAccountOfUserSpecifiedSubscription
    }

    Write-VerboseWithTime 'Restore-Subscription: fine'
}


<#
.SYNOPSIS
Convalida il file di configurazione e restituisce una tabella hash dei valori del file di configurazione.

.DESCRIPTION
La funzione Read-ConfigFile esegue la convalida del file di configurazione JSON e restituisce una tabella hash dei valori selezionati.
-- Viene innanzitutto eseguita la conversione del file JSON in un oggetto PSCustomObject. La tabella hash del sito Web dispone delle seguenti chiavi:
-- Location: Percorso del sito Web
-- Databases: Database del sito Web

.PARAMETER  ConfigurationFile
Specifica il percorso e il nome del file di configurazione JSON per il progetto Web. Visual Studio genera il file di configurazione JSON automaticamente alla creazione di un progetto Web e lo archivia nella cartella PublishScripts nella soluzione.

.PARAMETER HasWebDeployPackage
Indica che esiste un file ZIP del pacchetto di Distribuzione Web per l'applicazione Web. Per specificare un valore $true, utilizzare -HasWebDeployPackage o HasWebDeployPackage:$true. Per specificare un valore false, utilizzare HasWebDeployPackage:$false. Il parametro è obbligatorio.

.INPUTS
Nessuno. Non è possibile utilizzare l'input per questa funzione.

.OUTPUTS
System.Collections.Hashtable

.EXAMPLE
PS C:\> Read-ConfigFile -ConfigurationFile <path> -HasWebDeployPackage


Name                           Value                                                                                                                                                                     
----                           -----                                                                                                                                                                     
databases                      {@{connectionStringName=; databaseName=; serverName=; user=; password=}}                                                                                                  
website                        @{name="mysite"; location="West US";}                                                      
#>
function Read-ConfigFile
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateScript({Test-Path $_ -PathType Leaf})]
        [String]
        $ConfigurationFile
    )

    Write-VerboseWithTime 'Read-ConfigFile: inizio'

    # Ottieni il contenuto del file JSON (-raw ignora le interruzioni di riga) e convertilo in PSCustomObject
    $config = Get-Content $ConfigurationFile -Raw | ConvertFrom-Json

    if (!$config)
    {
        throw ('Read-ConfigFile: ConvertFrom-Json non riuscito: ' + $error[0])
    }

    # Determinare se l'oggetto environmentSettings dispone di proprietà 'webSite' (a prescindere dal valore della proprietà)
    $hasWebsiteProperty =  Test-Member -Object $config.environmentSettings -Member 'webSite'

    if (!$hasWebsiteProperty)
    {
        throw 'Read-ConfigFile: il file di configurazione non dispone di una proprietà webSite.'
    }

    # Crea una tabella hash dai valori in PSCustomObject
    $returnObject = New-Object -TypeName Hashtable

    $returnObject.Add('name', $config.environmentSettings.webSite.name)
    $returnObject.Add('location', $config.environmentSettings.webSite.location)

    if (Test-Member -Object $config.environmentSettings -Member 'databases')
    {
        $returnObject.Add('databases', $config.environmentSettings.databases)
    }

    Write-VerboseWithTime 'Read-ConfigFile: fine'

    return $returnObject
}


<#
.SYNOPSIS
Crea un sito Web di Windows Azure.

.DESCRIPTION
Crea un sito Web di Windows Azure con il percorso e il nome specificati. Questa funzione chiama il cmdlet New-AzureWebsite nel modulo Azure. Se la sottoscrizione non dispone di un sito Web con il nome specificato, il sito Web viene creato e viene restituito un oggetto sito Web. In caso contrario, viene restituito il sito Web esistente.

.PARAMETER  Name
Specifica un nome per il nuovo sito Web. Il nome deve essere univoco in Windows Azure. Questo parametro è obbligatorio.

.PARAMETER  Location
Specifica la posizione del sito Web. Sono valori validi le posizioni di Windows Azure, ad esempio "Stati Uniti occidentali". L'impostazione predefinita è la posizione della sottoscrizione. Questo parametro è obbligatorio.

.INPUTS
NONE.

.OUTPUTS
Microsoft.WindowsAzure.Commands.Utilities.Websites.Services.WebEntities.Site

.EXAMPLE
Add-AzureWebsite -Name TestSite -Location "West US"

Name       : contoso
State      : Running
Host Names : contoso.azurewebsites.net

.LINK
New-AzureWebsite
#>
function Add-AzureWebsite
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [String]
        $Name,

        [Parameter(Mandatory = $true)]
        [String]
        $Location
    )

    Write-VerboseWithTime 'Add-AzureWebsite: inizio'
    $website = Get-AzureWebsite -Name $Name -ErrorAction SilentlyContinue

    if ($website)
    {
        Write-HostWithTime ('Add-AzureWebsite: sito Web esistente ' +
        $website.Name + ' esistente')
    }
    else
    {
        if (Test-AzureName -Website -Name $Name)
        {
            Write-ErrorWithTime ('Il sito Web {0} esiste già' -f $Name)
        }
        else
        {
            $website = New-AzureWebsite -Name $Name -Location $Location
        }
    }

    $website | Out-String | Write-VerboseWithTime
    Write-VerboseWithTime 'Add-AzureWebsite: fine'

    return $website
}

<#
.SYNOPSIS
Restituisce $True quando l'URL è assoluto e il relativo schema è https.

.DESCRIPTION
La funzione Test-HttpsUrl converte l'URL di input in un oggetto System.Uri. Restituisce $True quando l'URL è assoluto (non relativo) e il relativo schema è https. Se è false, o se non è possibile convertire la stringa di input in un URL, la funzione restituisce $false.

.PARAMETER Url
Specifica l'URL da sottoporre a test. Immettere una stringa URL,

.INPUTS
NONE.

.OUTPUTS
System.Boolean

.EXAMPLE
PS C:\>$profile.publishUrl
waws-prod-bay-001.publish.azurewebsites.windows.net:443

PS C:\>Test-HttpsUrl -Url 'waws-prod-bay-001.publish.azurewebsites.windows.net:443'
False
#>
function Test-HttpsUrl
{

    param
    (
        [Parameter(Mandatory = $true)]
        [String]
        $Url
    )

    # Se non è possibile convertire $uri in un oggetto System.Uri, Test-HttpsUrl restituisce $false
    $uri = $Url -as [System.Uri]

    return $uri.IsAbsoluteUri -and $uri.Scheme -eq 'https'
}


<#
.SYNOPSIS
Crea una stringa che consente di connettersi a un database SQL di Windows Azure.

.DESCRIPTION
La funzione Get-AzureSQLDatabaseConnectionString assembla una stringa di connessione a un database SQL di Windows Azure.

.PARAMETER  DatabaseServerName
Specifica il nome di un server di database esistente nella sottoscrizione Windows Azure. Tutti i database di Windows Azure devono essere associati a un server di database SQL. Per ottenere il nome del server, utilizzare il cmdlet Get-AzureSqlDatabaseServer (modulo Azure). Questo parametro è obbligatorio.

.PARAMETER  DatabaseName
Specifica il nome per il database SQL. Questo può essere un database SQL esistente o un nome utilizzato per un nuovo database SQL. Questo parametro è obbligatorio.

.PARAMETER  Username
Specifica il nome dell'amministratore del database SQL. Il nome utente sarà $Username@DatabaseServerName. Questo parametro è obbligatorio.

.PARAMETER  Password
Specifica una password per l'amministratore del database SQL. Immettere una password come testo normale. Le stringhe sicure non sono consentite. Questo parametro è obbligatorio.

.INPUTS
Nessuno.

.OUTPUTS
System.String

.EXAMPLE
PS C:\> $ServerName = (Get-AzureSqlDatabaseServer).ServerName[0]
PS C:\> Get-AzureSQLDatabaseConnectionString -DatabaseServerName $ServerName `
        -DatabaseName 'testdb' -UserName 'admin'  -Password 'password'

Server=tcp:testserver.database.windows.net,1433;Database=testdb;User ID=admin@testserver;Password=password;Trusted_Connection=False;Encrypt=True;Connection Timeout=20;
#>
function Get-AzureSQLDatabaseConnectionString
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [String]
        $DatabaseServerName,

        [Parameter(Mandatory = $true)]
        [String]
        $DatabaseName,

        [Parameter(Mandatory = $true)]
        [String]
        $UserName,

        [Parameter(Mandatory = $true)]
        [String]
        $Password
    )

    return ('Server=tcp:{0}.database.windows.net,1433;Database={1};' +
           'User ID={2}@{0};' +
           'Password={3};' +
           'Trusted_Connection=False;' +
           'Encrypt=True;' +
           'Connection Timeout=20;') `
           -f $DatabaseServerName, $DatabaseName, $UserName, $Password
}


<#
.SYNOPSIS
Crea dei database SQL di Windows Azure dai valori presenti nel file di configurazione JSON generato da Visual Studio.

.DESCRIPTION
La funzione Add-AzureSQLDatabases recupera informazioni dalla sezione del file JSON relativa ai database. Questa funzione Add-AzureSQLDatabases (plurale) chiama la funzione Add-AzureSQLDatabase (singolare) per ciascun database SQL nel file JSON. Add-AzureSQLDatabase (singolare) chiama il cmdlet New-AzureSqlDatabase (modulo Azure) che crea i database SQL. Questa funzione non restituisce un oggetto di database. Restituisce una tabella hash di valori che sono stati utilizzati per la creazione dei database.

.PARAMETER DatabaseConfig
 Accetta una matrice di oggetti PSCustomObjects che hanno origine nel file JSON restituito dalla funzione Read-ConfigFile quando il file JSON presenta una proprietà di sito Web. Include le proprietà environmentSettings.databases. È possibile utilizzare l'elenco per questa funzione.
PS C:\> $config = Read-ConfigFile <name>.json
PS C:\> $DatabaseConfig = $config.databases| where {$_.connectionStringName}
PS C:\> $DatabaseConfig
connectionStringName: Default Connection
databasename : TestDB1
edition   :
size     : 1
collation  : SQL_Latin1_General_CP1_CI_AS
servertype  : New SQL Database Server
servername  : r040tvt2gx
user     : dbuser
password   : Test.123
location   : West US

.PARAMETER  DatabaseServerPassword
Specifica la password per l'amministratore del server di database SQL. Immettere una hashtable con le chiavi Nome e Password. Il valore di Name è il nome del server di database SQL. Il valore di Password è la password dell'amministratore. Ad esempio, @Name = "TestDB1"; Password = "password". Il parametro è facoltativo. Se viene omesso o se il nome del server di database SQL non corrisponde al valore della proprietà serverName dell'oggetto $DatabaseConfig, la funzione utilizza la proprietà Password dell'oggetto $DatabaseConfig per il database SQL nella stringa di connessione.

.PARAMETER CreateDatabase
Verifica che si desideri creare un database. Questo parametro è facoltativo.

.INPUTS
System.Collections.Hashtable[]

.OUTPUTS
System.Collections.Hashtable

.EXAMPLE
PS C:\> $config = Read-ConfigFile <name>.json
PS C:\> $DatabaseConfig = $config.databases| where {$_.connectionStringName}
PS C:\> $DatabaseConfig | Add-AzureSQLDatabases

Name                           Value
----                           -----
ConnectionString               Server=tcp:testdb1.database.windows.net,1433;Database=testdb;User ID=admin@testdb1;Password=password;Trusted_Connection=False;Encrypt=True;Connection Timeout=20;
Name                           Default Connection
Type                           SQLAzure

.LINK
Get-AzureSQLDatabaseConnectionString

.LINK
Create-AzureSQLDatabase
#>
function Add-AzureSQLDatabases
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [PSCustomObject]
        $DatabaseConfig,

        [Parameter(Mandatory = $false)]
        [AllowNull()]
        [Hashtable[]]
        $DatabaseServerPassword,

        [Parameter(Mandatory = $false)]
        [Switch]
        $CreateDatabase = $false
    )

    begin
    {
        Write-VerboseWithTime 'Add-AzureSQLDatabases: inizio'
    }
    process
    {
        Write-VerboseWithTime ('Add-AzureSQLDatabases: creazione ' + $DatabaseConfig.databaseName)

        if ($CreateDatabase)
        {
            # Crea un nuovo database SQL con i valori DatabaseConfig, a meno che non ne esista già uno
            # L'output dei comandi non viene visualizzato.
            Add-AzureSQLDatabase -DatabaseConfig $DatabaseConfig | Out-Null
        }

        $serverPassword = $null
        if ($DatabaseServerPassword)
        {
            foreach ($credential in $DatabaseServerPassword)
            {
               if ($credential.Name -eq $DatabaseConfig.serverName)
               {
                   $serverPassword = $credential.password             
                   break
               }
            }               
        }

        if (!$serverPassword)
        {
            $serverPassword = $DatabaseConfig.password
        }

        return @{
            Name = $DatabaseConfig.connectionStringName;
            Type = 'SQLAzure';
            ConnectionString = Get-AzureSQLDatabaseConnectionString `
                -DatabaseServerName $DatabaseConfig.serverName `
                -DatabaseName $DatabaseConfig.databaseName `
                -UserName $DatabaseConfig.user `
                -Password $serverPassword }
    }
    end
    {
        Write-VerboseWithTime 'Add-AzureSQLDatabases: fine'
    }
}


<#
.SYNOPSIS
Crea un nuovo database SQL di Windows Azure.

.DESCRIPTION
La funzione Add-AzureSQLDatabase crea un database SQL di Windows Azure dai dati nel file di configurazione JSON generato da Visual Studio e restituisce il nuovo database. Se la sottoscrizione dispone già di un database SQL con il nome di database specificato nel server di database SQL specificato, la funzione restituisce il database esistente. Questa funzione chiama il cmdlet New-AzureSqlDatabase (modulo Azure) che crea effettivamente il database SQL.

.PARAMETER DatabaseConfig
Utilizza un oggetto PSCustomObject che ha origine nel file di configurazione JSON restituito dalla funzione Read-ConfigFile quando il file JSON presenta una proprietà di sito Web. Include le proprietà environmentSettings.databases. Non è possibile utilizzare l'oggetto per questa funzione. Visual Studio genera un file di configurazione JSON per tutti i progetti Web e lo archivia nella cartella PublishScripts della soluzione.

.INPUTS
Nessuno. Questa funzione non riceve input dalla pipeline

.OUTPUTS
Microsoft.WindowsAzure.Commands.SqlDatabase.Services.Server.Database

.EXAMPLE
PS C:\> $config = Read-ConfigFile <name>.json
PS C:\> $DatabaseConfig = $config.databases | where connectionStringName
PS C:\> $DatabaseConfig

connectionStringName    : Default Connection
databasename : TestDB1
edition      :
size         : 1
collation    : SQL_Latin1_General_CP1_CI_AS
servertype   : New SQL Database Server
servername   : r040tvt2gx
user         : dbuser
password     : Test.123
location     : West US

PS C:\> Add-AzureSQLDatabase -DatabaseConfig $DatabaseConfig

.LINK
Add-AzureSQLDatabases

.LINK
New-AzureSQLDatabase
#>
function Add-AzureSQLDatabase
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateNotNull()]
        [Object]
        $DatabaseConfig
    )

    Write-VerboseWithTime 'Add-AzureSQLDatabase: inizio'

    # Interrompi se il valore di parametro non presenta la proprietà serverName o se questa non è popolata.
    if (-not (Test-Member $DatabaseConfig 'serverName') -or -not $DatabaseConfig.serverName)
    {
        throw 'Add-AzureSQLDatabase: serverName del database (obbligatorio) mancante dal valore DatabaseConfig.'
    }

    # Interrompi se il valore di parametro non presenta la proprietà databasename o se il valore di questa proprietà non è specificato.
    if (-not (Test-Member $DatabaseConfig 'databaseName') -or -not $DatabaseConfig.databaseName)
    {
        throw 'Add-AzureSQLDatabase: nome database (obbligatorio) mancante dal valore DatabaseConfig.'
    }

    $DbServer = $null

    if (Test-HttpsUrl $DatabaseConfig.serverName)
    {
        $absoluteDbServer = $DatabaseConfig.serverName -as [System.Uri]
        $subscription = Get-AzureSubscription -Current -ErrorAction SilentlyContinue

        if ($subscription -and $subscription.ServiceEndpoint -and $subscription.SubscriptionId)
        {
            $absoluteDbServerRegex = 'https:\/\/{0}\/{1}\/services\/sqlservers\/servers\/(.+)\.database\.windows\.net\/databases' -f `
                                     $subscription.serviceEndpoint.Host, $subscription.SubscriptionId

            if ($absoluteDbServer -match $absoluteDbServerRegex -and $Matches.Count -eq 2)
            {
                 $DbServer = $Matches[1]
            }
        }
    }

    if (!$DbServer)
    {
        $DbServer = $DatabaseConfig.serverName
    }

    $db = Get-AzureSqlDatabase -ServerName $DbServer -DatabaseName $DatabaseConfig.databaseName -ErrorAction SilentlyContinue

    if ($db)
    {
        Write-HostWithTime ('Create-AzureSQLDatabase: utilizzo del database esistente ' + $db.Name)
        $db | Out-String | Write-VerboseWithTime
    }
    else
    {
        $param = New-Object -TypeName Hashtable
        $param.Add('serverName', $DbServer)
        $param.Add('databaseName', $DatabaseConfig.databaseName)

        if ((Test-Member $DatabaseConfig 'size') -and $DatabaseConfig.size)
        {
            $param.Add('MaxSizeGB', $DatabaseConfig.size)
        }
        else
        {
            $param.Add('MaxSizeGB', 1)
        }

        # Se l'oggetto $DatabaseConfig presenta una proprietà collation e non è null o vuoto
        if ((Test-Member $DatabaseConfig 'collation') -and $DatabaseConfig.collation)
        {
            $param.Add('Collation', $DatabaseConfig.collation)
        }

        # Se l'oggetto $DatabaseConfig presenta una proprietà edition e non è null o vuoto
        if ((Test-Member $DatabaseConfig 'edition') -and $DatabaseConfig.edition)
        {
            $param.Add('Edition', $DatabaseConfig.edition)
        }

        # Scrivi la tabella hash nel flusso dettagliato
        $param | Out-String | Write-VerboseWithTime
        # Chiama New-AzureSqlDatabase con splatting (l'output non viene visualizzato)
        $db = New-AzureSqlDatabase @param
    }

    Write-VerboseWithTime 'Add-AzureSQLDatabase: fine'
    return $db
}
